package wiki.xsx.core.bytecode.handler;

import lombok.SneakyThrows;
import wiki.xsx.core.bytecode.BytecodeUtil;
import wiki.xsx.core.bytecode.enums.ExecuteType;
import wiki.xsx.core.bytecode.model.Annotations;
import wiki.xsx.core.bytecode.model.ParameterAnnotations;
import wiki.xsx.core.bytecode.transformer.ConstructorAnnotationTransformer;
import wiki.xsx.core.bytecode.transformer.ConstructorParamAnnotationTransformer;
import wiki.xsx.core.bytecode.transformer.ConstructorTransformer;

import java.util.List;

/**
 * 构造方法助手
 *
 * @author xsx
 * @date 2023/3/2
 * @since 1.8
 * <p>
 * Copyright (c) 2023 xsx All Rights Reserved.
 * easy-bytecode is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * </p>
 */
public class ConstructorHandler extends AbstractHandler {

    /**
     * 指定位置插入（插入指定行代码）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param lineNum        行号（源代码行号）
     * @param source         源代码
     */
    @SneakyThrows
    public void insertAt(String className, List<Class<?>> parameterTypes, Integer lineNum, String source) {
        this.check(className, lineNum, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_AT,
                        className,
                        parameterTypes,
                        lineNum,
                        source
                )
        );
    }

    /**
     * 方法之前插入（方法首行插入）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param source         源代码
     */
    @SneakyThrows
    public void insertBefore(String className, List<Class<?>> parameterTypes, String source) {
        this.check(className, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_BEFORE,
                        className,
                        parameterTypes,
                        source
                )
        );
    }

    /**
     * 方法之后插入（方法返回前插入）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param source         源代码
     */
    @SneakyThrows
    public void insertAfter(String className, List<Class<?>> parameterTypes, String source) {
        this.check(className, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_AFTER,
                        className,
                        parameterTypes,
                        source
                )
        );
    }

    /**
     * 方法之后插入（方法异常抛出后插入）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param source         源代码
     */
    @SneakyThrows
    public void insertAfterAsFinally(String className, List<Class<?>> parameterTypes, String source) {
        this.check(className, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_AFTER_FINALLY,
                        className,
                        parameterTypes,
                        source
                )
        );
    }

    /**
     * 方法环绕插入（方法前后插入）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param beforeSource   之前的源代码
     * @param afterSource    之后的源代码
     */
    @SneakyThrows
    public void insertAround(String className, List<Class<?>> parameterTypes, String beforeSource, String afterSource) {
        this.check(className, beforeSource, afterSource);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_AROUND,
                        className,
                        parameterTypes,
                        beforeSource,
                        afterSource
                )
        );
    }

    /**
     * 方法环绕插入（方法前、异常抛出后插入）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param beforeSource   之前的源代码
     * @param afterSource    之后的源代码
     */
    @SneakyThrows
    public void insertAroundAsFinally(String className, List<Class<?>> parameterTypes, String beforeSource, String afterSource) {
        this.check(className, beforeSource, afterSource);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_INSERT_AROUND_FINALLY,
                        className,
                        parameterTypes,
                        beforeSource,
                        afterSource
                )
        );
    }

    /**
     * 替换指定位置（替换指定行代码）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param source         源代码
     */
    public void replaceAt(String className, List<Class<?>> parameterTypes, Integer lineNum, String source) {
        this.check(className, lineNum, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_REPLACE_AT,
                        className,
                        parameterTypes,
                        lineNum,
                        source
                )
        );
    }

    /**
     * 替换全部（方法体替换）
     *
     * @param className      全类名
     * @param parameterTypes 参数类型列表
     * @param source         源代码
     */
    public void replaceAll(String className, List<Class<?>> parameterTypes, String source) {
        this.check(className, source);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_REPLACE_ALL,
                        className,
                        parameterTypes,
                        source
                )
        );
    }

    /**
     * 添加异常捕获
     *
     * @param className          全类名
     * @param parameterTypes     参数类型列表
     * @param source             源代码（catch块中）
     * @param exceptionClassName 异常类型名称
     */
    public void addCatch(String className, List<Class<?>> parameterTypes, String source, String exceptionClassName) {
        this.check(className, source, exceptionClassName);
        BytecodeUtil.transform(
                new ConstructorTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.METHOD_ADD_CATCH,
                        className,
                        parameterTypes,
                        source,
                        exceptionClassName
                )
        );
    }

    /**
     * 添加或更新注解
     *
     * @param className      类名称
     * @param parameterTypes 参数类型列表
     * @param annotations    注解
     */
    public void addOrUpdateAnnotation(String className, List<Class<?>> parameterTypes, Annotations... annotations) {
        this.check(className, annotations);
        BytecodeUtil.transform(
                new ConstructorAnnotationTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.ANNOTATION_METHOD_ADD_OR_UPDATE,
                        className,
                        parameterTypes,
                        annotations
                )
        );
    }

    /**
     * 删除注解
     *
     * @param className      类名称
     * @param parameterTypes 参数类型列表
     * @param annotations    注解
     */
    public void removeAnnotation(String className, List<Class<?>> parameterTypes, Annotations... annotations) {
        this.check(className, annotations);
        BytecodeUtil.transform(
                new ConstructorAnnotationTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.ANNOTATION_METHOD_REMOVE,
                        className,
                        parameterTypes,
                        annotations
                )
        );
    }

    /**
     * 添加或更新参数注解
     *
     * @param className      类名称
     * @param parameterTypes 参数类型列表
     * @param annotations    注解
     */
    public void addOrUpdateParamAnnotation(String className, List<Class<?>> parameterTypes, ParameterAnnotations... annotations) {
        this.check(className, annotations);
        BytecodeUtil.transform(
                new ConstructorParamAnnotationTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.ANNOTATION_METHOD_PARAM_ADD_OR_UPDATE,
                        className,
                        parameterTypes,
                        annotations
                )
        );
    }

    /**
     * 删除参数注解
     *
     * @param className      类名称
     * @param parameterTypes 参数类型列表
     * @param annotations    注解
     */
    public void removeParamAnnotation(String className, List<Class<?>> parameterTypes, ParameterAnnotations... annotations) {
        this.check(className, annotations);
        BytecodeUtil.transform(
                new ConstructorParamAnnotationTransformer(
                        this.isWrite,
                        this.fileDir,
                        ExecuteType.ANNOTATION_METHOD_PARAM_REMOVE,
                        className,
                        parameterTypes,
                        annotations
                )
        );
    }
}
